//
// Created by antler on 2018/11/6.
//

#ifndef LUA_LuaJavaHelper_H
#define LUA_LuaJavaHelper_H

#include <string>

#include <jni.h>

#include "lua.hpp"
#include "luaext.hpp"

extern "C" {
#include "lua_cjson.h"
};

#include "JNIDef.h"

#include "log.h"

#include "ScopedLocalRef.h"
#include "ScopedUtfChars.h"

#include "utils/ZipUtils.h"

class LuaJavaHelper {
protected:
    struct tlsThreadInfo {
        tlsThreadInfo()
                : env(nullptr) {}

        JNIEnv *env;
    };

public:
    static LuaJavaHelper *getInstance();

    static void releaseInstance();

public:
    ~LuaJavaHelper();

    void onLoad(JavaVM *javaVM);

    void onUnload(JavaVM *javaVM);

    void openLibs();

    void closeLibs();

    void setPackagePath(JNIEnv *env, jstring jpath);

    void setPackageCPath(JNIEnv *env, jstring jcpath);

    JavaVM *getJavaVM();

    JNIEnv *getEnv();

    lua_State *getLuaState();

    void detachCurrentThread(void *value);

    void closeLuaState();

    bool isStopFlag();

    void setStopFlag(bool value);

    void stop();

    void sleep(long timeoutMS);

    jint doFile(JNIEnv *env, jobject jobj, jstring jfilepath);

    jint doString(JNIEnv *env, jobject jobj, jstring jstr);

    void sendEvent(JNIEnv *env, jobject jobj, jstring jid, jint jaction, jint jfunID);

    void removeActionCallback(JNIEnv *env, jobject jobj, jint jaction, jint jfunID);

    jobject getCallbackObj();

    void setCallbackObj(jobject jobj);

    void setXXTEAKeyAndSign(const char *key, int keyLen, const char *sign, int signLen);

    void cleanupXXTEAKeyAndSign();

public:
    static void
    callVoidMethod(JNIEnv *env, jobject jobj, const char *methodName, const char *sig, ...);

    static jobject
    callObjectMethod(JNIEnv *env, jobject jobj, const char *methodName, const char *sig, ...);

    static void callStaticVoidMethod(JNIEnv *env, const char *className, const char *methodName,
                                     const char *sig, ...);

    static jobject
    callStaticObjectMethod(JNIEnv *env, const char *className, const char *methodName,
                           const char *sig, ...);

    static jboolean
    callStaticBooleanMethod(JNIEnv *env, const char *className, const char *methodName,
                            const char *sig, ...);

    static jint callStaticIntMethod(JNIEnv *env, const char *className, const char *methodName,
                                    const char *sig, ...);

    static int runShellCommand(JNIEnv *env, const char *command, bool isRoot, std::string *pResult,
                               int timeoutMS);

    static std::string jstring2string(JNIEnv *env, jstring jstr);

protected:
    LuaJavaHelper();

    void setJavaVM(JavaVM *javaVM);

    JNIEnv *getEnvFromTls();

    void setEnvToTls(JNIEnv *env);

    JNIEnv *cacheEnv(JavaVM *javaVM);

    lua_State *getLuaStateFromTls();

    void setLuaStateToTls(lua_State *L);

    lua_State *cacheLuaState();

    tlsThreadInfo *getTlsThreadInfo(bool create);

    static int searcher_assets(lua_State *L);

    void addAssetsLoader(lua_State *L);

    int doZipfile(lua_State *L, const char *zipFilePath);

    int luaLoadBuffer(lua_State *L, const char *chunk, int chunkSize, const char *chunkName);

protected:
    static LuaJavaHelper *g_luaJavaHelper;

    JavaVM *_pJavaVM;
    jobject _jobjCallback;
    lua_State *_luaState;

    volatile bool _stop;

    pthread_key_t _key;
    pthread_cond_t _cond;
    pthread_mutex_t _mutex;

    bool _xxteaEnabled;
    char *_xxteaKey;
    int _xxteaKeyLen;
    char *_xxteaSign;
    int _xxteaSignLen;

    ZipFile *_zipFile;
};

#endif //LUA_LuaJavaHelper_H
